package hu.jonas.junitmax;

import hu.jonas.junitmax.model.TestResultContentProvider;
import hu.jonas.junitmax.model.TestResultModel;

import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaModel;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.junit.ui.JUnitProgressBar;
import org.eclipse.jdt.junit.JUnitCore;
import org.eclipse.jdt.junit.TestRunListener;
import org.eclipse.jdt.junit.model.ITestCaseElement;
import org.eclipse.jdt.junit.model.ITestElement;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.ViewPart;

@SuppressWarnings("restriction")
public class TestResultViewPart extends ViewPart {

	public static final String VIEWPART_ID = "hu.jonas.junitmax.resultview";
	private static final PassedTestFilter PASSED_TEST_FILTER = new PassedTestFilter();
	
	private TableViewer testTable;
	private JUnitProgressBar progressBar;
	
	private TestResultModel testResultModel = new TestResultModel();
	private TestResultSorter testResultSorter = new TestResultSorter();
	
	private int failedTests = 0;
	
	public TestResultViewPart() {
	}

	@Override
	public void createPartControl(Composite parent) {

		GridLayout layout = new GridLayout();
		parent.setLayout(layout);
		
		progressBar = new JUnitProgressBar(parent);
		progressBar.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
		
		testTable = createTestResultTable(parent);
		testTable.getTable().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
		
		// TODO: clear test listener at dispose
		JUnitCore.addTestRunListener(new TestRunListener() {

			@Override
			public void testCaseFinished(final ITestCaseElement testCaseElement) {
				getViewSite().getShell().getDisplay().syncExec(new Runnable() {

					@Override
					public void run() {
						Activator.log(
								testCaseElement.getTestClassName() + "." + testCaseElement.getTestMethodName()
							);
						
						testResultModel.add(testCaseElement);
						testTable.setInput(testResultModel);
						
						if (testCaseElement.getTestResult(false) == ITestElement.Result.ERROR ||
								testCaseElement.getTestResult(false) == ITestElement.Result.FAILURE) {
							
							failedTests++;
						}
						
						progressBar.step(failedTests);
					}
				});
			}
			
		});
	}
	
	@Override
	public void setFocus() {
	}
	
	private TableViewer createTestResultTable(Composite parent) {
		
		TableViewer result = new TableViewer(parent, SWT.FULL_SELECTION);
		
		TableViewerColumn column = new TableViewerColumn(result, SWT.NONE);
		column.getColumn().setText("Test class name");
		column.getColumn().setWidth(200);
		
		column = new TableViewerColumn(result, SWT.NONE);
		column.getColumn().setText("Test method name");
		column.getColumn().setWidth(200);
		
		column = new TableViewerColumn(result, SWT.NONE);
		column.getColumn().setText("Elapsed time");
		column.getColumn().setWidth(100);
		column.getColumn().setAlignment(SWT.RIGHT);

		result.getTable().setHeaderVisible(true);
		result.getTable().setLinesVisible(true);
		result.setLabelProvider(new TestResultTableLabelProvider());
		result.setContentProvider(new TestResultContentProvider());
		result.setSorter(testResultSorter);
		
		result.addDoubleClickListener(new IDoubleClickListener() {
			
			@Override
			public void doubleClick(DoubleClickEvent event) {
				ISelection selection = event.getSelection();
				
				if (selection.isEmpty()) {
					return;
				}
				
				if (selection instanceof StructuredSelection) {
					StructuredSelection structuredSelection = (StructuredSelection) selection;
					ITestCaseElement firstElement = (ITestCaseElement) structuredSelection.getFirstElement();
					
					jumpToJavaMethod(firstElement.getTestClassName(), firstElement.getTestMethodName());
				}
				
			}
		});
		
		return result;
	}
	
	public void startRunTests(int numberOfTests) {
		testResultModel.clear();
		testTable.setInput(testResultModel);
		failedTests = 0;
		progressBar.reset(false, false, 0, numberOfTests);
	}
	
	public void setPassedTestFilter(boolean switched) {
		if (switched) {
			testTable.addFilter(PASSED_TEST_FILTER);
		} else {
			testTable.removeFilter(PASSED_TEST_FILTER);
		}
	}
	
	public void jumpToJavaMethod(String className, String methodName) {
		IMethod method = findJavaMethod(className, methodName);
		
		try {
			ICompilationUnit compilationUnit = method.getCompilationUnit();
			IEditorPart editorPart = JavaUI.openInEditor(compilationUnit);
			JavaUI.revealInEditor(editorPart, (IJavaElement) method);
			
		} catch (PartInitException e) {
			e.printStackTrace();
		} catch (JavaModelException e) {
			e.printStackTrace();
		}
		
		
	}
	
	private IMethod findJavaMethod(String className, String methodName) {
		IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
		IJavaModel javaModel = JavaCore.create(workspaceRoot);
		
		try {
			IJavaProject[] javaProjects = javaModel.getJavaProjects();
			for (IJavaProject javaProject : javaProjects) {
				IType type = javaProject.findType(className);
				
				if (type != null) {
					IMethod[] methods = type.getMethods();
					for (IMethod method : methods) {
						if (method.getElementName().equals(methodName)) {
							return method;
						}
					}
				}
			}
		} catch (JavaModelException e) {
			e.printStackTrace();
		}
		
		return null;
	}
	
}
